package redis

import (
	"context"
	"fmt"
	"strconv"
	"time"

	"github.com/go-redis/redis/v8"

	"mxshop_srvs/consul_srv/setting"
)

type ExistsErr struct{}

func (e ExistsErr) Error() string {
	return fmt.Sprint("key is exists")
}

// Redis defines a redis dao.
type Redis struct {
	rdb *redis.Client
}

// NewRedis creates a redis dao.
func NewRedis(rdb *redis.Client) *Redis {
	return &Redis{
		rdb: rdb,
	}
}

func Init() (rdb *redis.Client, err error) {
	redisConf := setting.Conf.RedisConfig
	rdb = redis.NewClient(&redis.Options{
		Addr:     fmt.Sprintf("%s:%d", redisConf.Host, redisConf.Port),
		Password: redisConf.Password, // no password set
		DB:       redisConf.DB,       // use default DB
	})

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	_, err = rdb.Ping(ctx).Result()
	return rdb, err
}

const (
	MxshopWorkerIncrKey = "mxshop_worker_incr"
	MxshopWorkerHashKey = "mxshop_worker_hash"
	MxshopWorkerListKey = "mxshop_worker_list"
	MxshopServiceTagKey = "mxshop_service_set"
)

func (r *Redis) RecordWorkerID(ctx context.Context, serviceID string, workerID int64) error {
	return r.rdb.HSetNX(ctx, MxshopWorkerHashKey, serviceID, workerID).Err()
}

func (r *Redis) NewWorkerID(ctx context.Context, serviceID string) (int64, error) {
	incr := r.rdb.Incr(ctx, MxshopWorkerIncrKey) // Incr自增获取workerID
	if err := incr.Err(); err != nil {
		return 0, err
	}
	err := r.RecordWorkerID(ctx, serviceID, incr.Val()) // 记录serviceID对应的workerID
	if err != nil {
		return 0, err
	}
	return incr.Val(), nil
}

func (r *Redis) GetWorkerID(ctx context.Context, serviceID string) (int64, error) {
	var val string
	val, err := r.rdb.HGet(ctx, MxshopWorkerHashKey, serviceID).Result() // 判断该serviceID是否有记录过的workerID
	if err != nil {
		if err != redis.Nil {
			return 0, err
		}
		// 查询列表
		val, err = r.rdb.LPop(ctx, MxshopWorkerListKey).Result() // 如果没有则去查未使用的workerID池
		if err != nil {
			return 0, err
		}
	}
	workerID, err := strconv.Atoi(val)
	if err != nil {
		return 0, err
	}
	return int64(workerID), nil
}

func (r *Redis) ReturnWorkerID(ctx context.Context, serviceID string, workerID int64) error {
	pipe := r.rdb.TxPipeline()
	pipe.HDel(ctx, MxshopWorkerHashKey, serviceID) // 删除serviceID记录
	pipe.RPush(ctx, MxshopWorkerListKey, workerID) // 添加到workerID池
	_, err := pipe.Exec(ctx)
	if err != nil {
		return err
	}
	return nil
}

func (r *Redis) AddTag(ctx context.Context, serviceID string) error {
	return r.rdb.SAdd(ctx, MxshopServiceTagKey, serviceID).Err()
}

func (r *Redis) GetTag(ctx context.Context, serviceID string) (bool, error) {
	return r.rdb.SIsMember(ctx, MxshopServiceTagKey, serviceID).Result()
}

func (r *Redis) DelTag(ctx context.Context, serviceID string) error {
	return r.rdb.SRem(ctx, MxshopServiceTagKey, serviceID).Err()
}
